var React = require('react');
var ReactDOM = require('react-dom');
var createReactClass = require('create-react-class');
var redux = require('redux');
var createStore = redux.createStore;
var combineReducers = redux.combineReducers;
var redux = require('react-redux')
var Provider = redux.Provider;
var connect = redux.connect;
const {clipboard} = require('electron');
var remote = require('electron').remote;
var app = require('electron').remote.app;
let shell = require('electron').shell;
var path = require('path');
var fs = require('fs');
var api = require('./api.js');
const {download} = remote.require('electron-dl');
const unusedFilename = require('unused-filename');

var CONVERSATION_PEER = "peer";
var CONVERSATION_GROUP = "group";


var ChatHistory = createReactClass({
    getInitialState: function() {
        return {
            messages:[],
            selected:undefined
        };
    },

    onDoubleClick: function(msg) {
        if (msg.contentObj.file && msg.contentObj.file.path) {
            var filePath = msg.contentObj.file.path;
            if (fs.existsSync(filePath)) {
                shell.openItem(filePath);
            }
        }
    },

    showContextMenu: function(msg, e) {
        e.preventDefault();
        e.stopPropagation();
        console.log("context menu event:", e);
        console.log("show context menu:" + msg);

        var menuTpl;
        if (msg.contentObj.text) {
            var target = e.target;
            menuTpl = [{
                    type: 'separator'
                },
                {
                    id: 'copy',
                    label: '复制',
                    click: () => {
                        var sel = getSelection();
                        if (sel && sel.toString() && sel.focusNode.parentNode == target) {
                            this.copyMessage(msg, sel.toString());
                        } else {
                            this.copyMessage(msg);
                        }
                    }
                },
                {
                    type: 'separator'
                },
            ];

        } else if (msg.contentObj.image2) {
            menuTpl = [{
                    type: 'separator'
                },
                {
                    id: 'save',
                    label: '保存图片',
                    click: () => {
                        this.downloadImage(msg);
                    }
                },
                {
                    type: 'separator'
                },
            ];

        } else if (msg.contentObj.file) {
            menuTpl = [{
                type: 'separator'
            }, {
                id: 'download',
                label: '下载',
                click: () => {
                    this.downloadFile(msg);
                }
            }, {
                type: 'separator'
            }];
        } else {
            return;
        }

        var menu = new remote.Menu.buildFromTemplate(menuTpl);
        menu.popup(remote.getCurrentWindow(), {
            async: true
        });

        menu.on('menu-will-close', () => {
            this.setState({selected:undefined});
        });
        this.setState({selected:msg});
    },

    //only support text
    copyMessage: function(msg, selectionText) {
        if (!msg.contentObj.text) {
            return;
        }
        if (selectionText) {
            console.log("write clipboard text:", selectionText);
            clipboard.writeText(selectionText);
        } else {
            console.log("write clipboard text:", msg.contentObj.text);
            clipboard.writeText(msg.contentObj.text);
        }
    },

    downloadImage: function(msg) {
        if (!msg.contentObj.image2) {
            return;
        }
        var srcURL = msg.contentObj.image2.url;
        console.log("save image:", srcURL);
        download(remote.getCurrentWindow(), srcURL);
    },

    downloadFile: function(msg) {
        if (!msg.contentObj.file) {
            return;
        }
        var filename = msg.contentObj.file.filename;

        var dir = app.getPath('downloads');
        var filePath = unusedFilename.sync(path.join(dir, filename));
        filename = path.basename(filePath);

		download(remote.getCurrentWindow(), msg.contentObj.file.url, {filename:filename})
            .then(() => {
                msg.contentObj.file.path = filePath;
                msg.content = JSON.stringify(msg.contentObj);
                if (this.props.conversation.type == CONVERSATION_PEER) {
                    this.props.imDB.updateContent(msg.msgLocalID, msg.content, msg.contentObj);
                } else if (this.props.conversation.type == CONVERSATION_GROUP) {
                    this.props.groupDB.updateContent(msg.msgLocalID, msg.content, msg.contentObj);
                }
            });

        console.log("download filename:", filename, " url:", msg.contentObj.file.url);
    },

    formatFileSize: function(size) {
        if (size < 1024*2) {
            return size + "字节";
        } else if (size < 1024*1024) {
            return (size/1024).toFixed(1) + 'KB';
        } else {
            return (size/(1024*1024)).toFixed(1) + 'MB';
        }
    },

    buildText: function (msg) {
        function createMarkup() { return {__html: msg.text}; };
        return (
            <div onContextMenu={this.showContextMenu.bind(this, msg)}
                className="bubble text">
                <p draggable="false" dangerouslySetInnerHTML={createMarkup()} className="text"></p>
            </div>
        );
    },

    buildImage: function (msg) {
        var image = msg.contentObj.image2;
        var w = image.width;
        var h = image.height;
        if (image.width > 300) {
            w = 300;
            h = (image.height/image.width)*300;
        }
        var style = {};
        if (w > 0 && h > 0) {
            style = {
                width:w + "px",
                height:h + "px"
            };
        }

        var imageURL = msg.image;
        if (msg.image.startsWith("http") && msg.image.includes("gobelieve.io")) {
            imageURL = msg.image + "@256w_256h_0c";
        } else if (msg.image.startsWith("http") && msg.image.includes("pab27ku7r.bkt.clouddn.com")) {
            imageURL = msg.image + "?imageView2/1/w/200/h/200";
        }

        return (
            <div onContextMenu={this.showContextMenu.bind(this, msg)}
                className="bubble">
                <a draggable="false" href={msg.image} target="_blank">
                    <img draggable="false" style={style} className="image-thumb-body" src={imageURL} />
                </a>
            </div>
        );
    },

    buildAudio: function (msg) {
        // console.log("收到语音消息++"+JSON.stringify(msg))
        var audio_url = msg.audio.url + ".mp3";
        return (
            <div className="bubble">
                <audio  controls="controls" controlsList="nodownload remoteplayback" src={audio_url}/>
            </div>
        );
    },

    buildFile: function (msg, onCancel) {
        var size;
        var uploading = false;
        if (!msg.error && !msg.canceled && msg.uploading) {
            uploading = true;
        }
        if (uploading) {
            size = this.formatFileSize(msg.progress) + "/" + this.formatFileSize(msg.file.size);
        } else {
            size = this.formatFileSize(msg.file.size);
        }
        return (
            <div onContextMenu={this.showContextMenu.bind(this, msg)}
                onDoubleClick={this.onDoubleClick.bind(this, msg)}
                draggable="false"
                href={msg.file.url}
                className="bubble"
                download={msg.file.filename}>
                <div className="file">
                    <img draggable="false" src="images/file.png" className=""></img>
                    <div>
                        <p className="name">{msg.file.filename}</p>
                        <p className="size">
                            <span>{size}</span>
                            {msg.canceled ? <span className="canceled">已取消</span> : null}
                        </p>
                    </div>
                    {uploading ? <img src="./images/cancel.png" className="cancel" data-id={msg.id} onClick={onCancel} /> : null}
                </div>
            </div>
        );
    },

    buildNotification: function(msg) {
        return (
           <div className="notification">{msg.notification}</div>
        );
    },

    buildLocation: function(msg, onclick) {
        var addr = msg.contentObj.location.address;
        if (!addr) {
            addr = "位置";
        }
        return (
            <div className="bubble">
                <div className="location">
                    <div className="address">{addr}</div>
                    <img className="marker" draggable="false" data-id={msg.id} onClick={onclick} src="./images/location.png" />
                </div>
            </div>
        );
    },

    buildVideo: function(msg, onclick) {
        var video = msg.contentObj.video;
        var w = video.width;
        var h = video.height;
        if (video.width > 300) {
            w = 300;
            h = (video.height/video.width)*300;
        }
        var style = {};
        var buttonStyle = {};
        if (w > 0 && h > 0) {
            style = {
                width:w + "px",
                height:h + "px"
            };
            buttonStyle = {
                position:"absolute",
                left:(w/2 - 24) + "px",
                top:(h/2 - 24) + "px",
                width: "48px",
                height: "48px"
            }
        }

        var thumbURL = video.thumbnail;
        if (thumbURL.startsWith("http") && thumbURL.includes("gobelieve.io")) {
            thumbURL = thumbURL + "@256w_256h_0c";
        }
        return (
            <div onContextMenu={this.showContextMenu.bind(this, msg)}
                className="bubble">
                <div style={{position:"relative"}}>
                    <img draggable="false" style={style} className="image-thumb-body" src={thumbURL} />
                    <img draggable="false" style={buttonStyle} data-id={msg.id} onClick={onclick} src="./images/play.png"/>
                </div>
            </div>
        );
    },

    buildUnknown: function (msg) {
        return (
            <div className="bubble">
                <p className="text">{"未知消息类型"}</p>
            </div>
        );
    },

    scrollDown: function() {
        var c = document.getElementById("chatHistory");
        var h = c.getElementsByTagName('ul').item(0).offsetHeight;
        c.scrollTop = h;
        document.getElementById("entry").textContent = "";
    },

    loadSenderInfo: function(msg) {
        var self = this;
        if (self.props.conversation.type == CONVERSATION_PEER) {
            if (msg.isOutgoing) {
                var avatar = self.props.loginUser.avatar ? self.props.loginUser.avatar : "./images/_avatar.png";
                msg.avatar = avatar;
            } else {
                msg.avatar = self.props.conversation.avatar ? self.props.conversation.avatar : "./images/_avatar.png";
                msg.name = self.props.conversation.name;
            }
        } else if(self.props.conversation.type == CONVERSATION_GROUP) {
            if (msg.isOutgoing) {
                var avatar = self.props.loginUser.avatar ? self.props.loginUser.avatar : "./images/_avatar.png";
                msg.avatar = avatar;
            } else {
                var u = self.props.userDB.findUser(msg.sender);
                if (u) {
                    msg.name = u.name;
                    msg.avatar = u.avatar ? u.avatar : "./images/_avatar.png";
                } else {
                    msg.avatar = "./images/_avatar.png";
                }
            }
        }
    },

    reverseGeocodeLocation: function(msg) {
        api.reverseGeocodeLocation(msg.contentObj.location.longitude, msg.contentObj.location.latitude)
            .then((address) => {
                console.log("address:", address);
                msg.contentObj.location.address = address;
                this.setMessageLocationAddress(msg.msgLocalID, address);

                if (this.props.conversation.type == CONVERSATION_PEER) {
                    this.props.imDB.updateContent(msg.msgLocalID, msg.content, msg.contentObj);
                } else {
                    this.props.groupDB.updateContent(msg.msgLocalID, msg.content, msg.contentObj);
                }
            })
            .catch((err) => {
                console.log("reverse geocode location:", err);
            })
    },

    initMessages: function(messages) {
        var arr = [];
        var time = 0;
        var self = this;
        var uploadingMessages = this.props.uploadingMessages;
        var cid = this.props.conversation.cid;

        //去除重复的消息
        var newMessages = []
        var uuidSet = new Map();
        messages.forEach(function(msg) {
            if (msg.contentObj.uuid) {
                if (!uuidSet.has(msg.contentObj.uuid)) {
                    newMessages.push(msg);
                    uuidSet.set(msg.contentObj.uuid, "");
                } else {
                    console.warn("repeat message:", msg);
                }
            } else {
                newMessages.push(msg);
            }
        });
        messages = newMessages;

        //消息状态
        messages.forEach(function(msg) {
            msg.isOutgoing = (self.props.loginUser.uid == msg.sender);
            if (msg.isOutgoing && !msg.canceled && !msg.ack && !msg.error) {
                if (msg.timestamp < self.props.uptime) {
                    msg.error = true;
                }
            }

            var upReq = uploadingMessages.find(function(item) {
                return (item.cid == cid && item.msgLocalID == msg.msgLocalID);
            });

            if (upReq) {
                msg.uploading = true;
                msg.progress = upReq.progress;
            }

            if (msg.contentObj.location && !msg.contentObj.location.address) {
                self.reverseGeocodeLocation(msg);
            }
            self.loadSenderInfo(msg);
        });

        messages.forEach(function(msg) {
            //10min间隔
            if (msg.timestamp - time > 10*60) {
                var t = helper.toTime(msg.timestamp * 1000)
                var obj = {
                    id:msg.id+"_t",
                    msgLocalID:msg.msgLocalID+"_t",
                    sender:0,
                    receiver:msg.receiver,
                    contentObj:{notification:t, timestamp:msg.timestamp},
                    timestamp:msg.timestamp
                };
                arr.push(obj);
                time = msg.timestamp;
            }
            arr.push(msg);
        });

        this.setState({messages:arr},
                      () => {
                          this.scrollDown();
                      });

    },

    componentWillMount: function() {
        if (this.props.conversation.type == CONVERSATION_PEER) {
            this.props.imDB.loadMessages(this.props.conversation.peer, (messages) => {
                this.initMessages(messages);
            });
        } else if (this.props.conversation.type == CONVERSATION_GROUP) {
            this.props.groupDB.loadMessages(this.props.conversation.groupID, (messages) => {
                this.initMessages(messages);
            });
        } else {
            this.setState({messages:[]});
        }
    },

    componentWillReceiveProps: function(nextProps) {
        if (this.props.conversation.cid == nextProps.conversation.cid) {
            return;
        }


        if (nextProps.conversation.type == CONVERSATION_PEER) {
            this.props.imDB.loadMessages(nextProps.conversation.peer, (messages) => {
                this.initMessages(messages);
            });
        } else if (nextProps.conversation.type == CONVERSATION_GROUP) {
            this.props.groupDB.loadMessages(nextProps.conversation.groupID, (messages) => {
                this.initMessages(messages);
            });
        } else {
            this.setState({messages:[]});
        }
    },

    appendMessage:function(msg) {
        var m = Object.assign({}, msg);
        m.id = msg.msgLocalID;

        if (!msg.sender) {
            m.cls = "middle";
            return this.buildMiddleMessage(m);
        } else if (this.props.loginUser.uid == msg.sender) {
            m.cls = "out";
            if (msg.error) {
                m.cls += " fail";
            } else if (msg.canceled) {
                m.cls += " canceled";
            } else if (msg.uploading) {
                m.cls += " uploading";
            } else if (!msg.ack) {
                m.cls += " spinning";
            }
            if (this.state.selected &&
                this.state.selected.msgLocalID == msg.msgLocalID) {
                m.cls += ' selected';
            }
            return this.buildOutMessage(m);
        } else {
            m.cls = "in";
            if (this.state.selected &&
                this.state.selected.msgLocalID == msg.msgLocalID) {
                m.cls += ' selected';
            }
            return this.buildInMessage(m);
        }
    },

    buildMessageContent: function(msg) {
        var m = msg;
        //console.log(m)
        if (msg.contentObj.text) {
            var inputHtml = util.toStaticHTML(msg.contentObj.text);
            inputHtml = util.urlify(inputHtml);
            m.text = inputHtml;
            return this.buildText(m);
        } else if (msg.contentObj.audio) {
            m.audio = msg.contentObj.audio;
            return this.buildAudio(m);
        } else if (msg.contentObj.image2) {
            m.image = msg.contentObj.image2.url;
            return this.buildImage(m);
        } else if (msg.contentObj.image) {
            m.image = msg.contentObj.image;
            return this.buildImage(m);
        } else if (msg.contentObj.file) {
            m.file = msg.contentObj.file;
            return this.buildFile(m, this.onCancel);
        } else if (msg.contentObj.notification) {
            m.notification = msg.contentObj.notification;
            return this.buildNotification(m);
        } else if (msg.contentObj.location) {
            return this.buildLocation(m, this.onLocation);
        } else if (msg.contentObj.video) {
            return this.buildVideo(m, this.onVideo);
        } else {
            console.log("unknown msg:", msg);
            return this.buildUnknown(m)
        }
    },

    buildOutMessage: function(msg) {
        return (
            <li className={"chat-item " + msg.cls} key={msg.msgLocalID} data-id={msg.id}>
                <img className={"avatar " + msg.cls} src={msg.avatar}></img>
                <div className={"message " + msg.cls}>
                    {this.buildMessageContent(msg)}
                </div>
            </li>
        );
    },

    buildInMessage: function(msg) {
        if (this.props.conversation.type == CONVERSATION_GROUP) {
            return (
                <li className={"chat-item " + msg.cls} key={msg.msgLocalID} data-id={msg.id}>
                    <img style={{top:"4px"}}className={"avatar " + msg.cls} src={msg.avatar}></img>
                    <div>
                        <div className="name">{msg.name}</div>
                        <div className={"message " + msg.cls}>
                            {this.buildMessageContent(msg)}
                        </div>
                    </div>
                </li>
            );
        } else {
            return (
                <li className={"chat-item " + msg.cls} key={msg.msgLocalID} data-id={msg.id}>
                    <img className={"avatar " + msg.cls} src={msg.avatar}></img>
                    <div className={"message " + msg.cls}>
                        {this.buildMessageContent(msg)}
                    </div>
                </li>
            );
        }
    },

    buildMiddleMessage: function(msg) {
        return (
            <li className={"chat-item " + msg.cls}  key={msg.msgLocalID} data-id={msg.id}>
        	      {this.buildMessageContent(msg)}
            </li>
        );
    },

    onCancel: function(e) {
        console.log("event:", e.target.getAttribute('data-id'));
        e.stopPropagation();

        var msgID = e.target.getAttribute('data-id');

        var msg = this.state.messages.find(function(msg) {
            return msg.msgLocalID == msgID;
        });
        if (!msg) {
            return;
        }
        console.log("cancel msg:", msg);
        this.props.cancelUploadFile(msg);
        this.setMessageCanceled(msg.msgLocalID, true);
    },

    onLocation: function(e) {
        console.log("location event msgid:", e.target.getAttribute('data-id'));
        e.stopPropagation();

        var msgID = e.target.getAttribute('data-id');
        var msg = this.state.messages.find(function(msg) {
            return msg.msgLocalID == msgID;
        });
        if (!msg) {
            return;
        }
        console.log("location msg:", msg);
        var loc = msg.contentObj.location;
        var name = "";
        if (msg.contentObj.location.address) {
            name = encodeURIComponent(msg.contentObj.location.address);
        } else {
            name = encodeURIComponent("位置");
        }
        var coord = encodeURIComponent(loc.longitude + "," + loc.latitude);
        var url = `http://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&name=${name}&pointy=${loc.latitude}&pointx=${loc.longitude}&coord=${coord}`;
        shell.openExternal(url);
    },

    onVideo: function(e) {
        console.log("location event msgid:", e.target.getAttribute('data-id'));
        e.stopPropagation();

        var msgID = e.target.getAttribute('data-id');
        var msg = this.state.messages.find(function(msg) {
            return msg.msgLocalID == msgID;
        });
        if (!msg) {
            return;
        }

        console.log("click video message:", msg);
        
        this.props.openVideoWindow(msg.contentObj.video.url);
    },

    addMessage: function(msg) {
        var messages = this.state.messages;
        var found = messages.find(function(m) {
            return (m.contentObj.uuid && msg.contentObj.uuid && m.contentObj.uuid == msg.contentObj.uuid);
        });
        if (found) {
            console.warn("add warn message:", msg);
            return;
        }

        var index = -1;
        for (var i = messages.length; i > 0; i--) {
            var m = messages[i-1];
            if (m.contentObj.timestamp) {
                index = i - 1;
                break;
            }
        }

        if (index == -1 ||
            msg.timestamp - messages[index].contentObj.timestamp > 10*60) {
            var t = helper.toTime(msg.timestamp * 1000)
            var obj = {
                id:msg.id+"_t",
                msgLocalID:msg.msgLocalID+"_t",
                sender:0,
                receiver:msg.receiver,
                contentObj:{notification:t, timestamp:msg.timestamp},
                timestamp:msg.timestamp
            };
            messages = messages.concat(obj);
        }

        msg.isOutgoing = (this.props.loginUser.uid == msg.sender);
        this.loadSenderInfo(msg);
        if (msg.contentObj.location && !msg.contentObj.location.address) {
            this.reverseGeocodeLocation(msg);
        }
        messages = messages.concat(msg);
        this.setState({messages:messages}, () => {
            this.scrollDown();
        });
    },

    ackMessage: function(msgLocalID) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].ack = true;
            this.setState({messages:messages});
        }
    },

    setMessageError: function(msgLocalID, error) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].error = error;
            this.setState({messages:messages});
        }
    },


    setMessageUploadProgress: function(msgLocalID, progress) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].progress = progress;
            this.setState({messages:messages});
        }
    },

    setMessageUploadComplete: function(msgLocalID) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].uploading = false;
            this.setState({messages:messages});
        }
    },

    setMessageCanceled: function(msgLocalID, canceled) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].canceled = canceled;
            this.setState({messages:messages});
        }
    },

    setMessageLocationAddress: function(msgLocalID, address) {
        var index = this.state.messages.findIndex((m) => {
            return m.msgLocalID == msgLocalID;
        });

        if (index == -1) {
            return;
        } else {
            var messages = this.state.messages;
            messages[index].contentObj.location.address = address;
            this.setState({messages:messages});
        }
    },

    render: function() {
        var nodes = [];
        var msgs = this.state.messages;
        console.log("render history:", msgs.length);
        for (var i in msgs) {
            var msg = msgs[i];
            var n = this.appendMessage(msg);
            if (n) {
                nodes.push(n);
            }
        }
        return (
            <div className="chat-list" id="chatHistory">
                <ul>
                    {nodes}
                </ul>
            </div>
        );
    },

});

var ChatHistory = connect(function(state) {
    return {
        conversation:state.conversation,
        loginUser:state.loginUser
    };
}, null, null, {withRef:true})(ChatHistory);

module.exports = ChatHistory;
